<template>
  <q-page>
    <div class="text-h5 text-center text-secondary q-py-sm">
      {{ room_name }}
    </div>
    <div class="row justify-center q-gutter-sm">
      <!-- 本地视频 -->
      <q-card
        class="my-card q-ma-sm q-pa-xs"
        style="background: radial-gradient(circle, #35a2ff 0%, #014a88 100%)"
      >
        <video
          class="bg-dark"
          :style="style"
          min-width="300px"
          autoplay
          playsinline
          muted="muted"
          ref="localVideo"
        ></video>

        <q-card-actions align="right">
          <q-btn flat round color="red" icon="favorite" />
          <q-btn flat round color="teal" icon="bookmark" />
          <q-btn flat round color="primary" icon="share" />
        </q-card-actions>
      </q-card>
      <!-- 远程视频 -->
      <q-card
        class="my-card q-ma-sm q-pa-xs"
        style="background: radial-gradient(circle, #35a2ff 0%, #014a88 100%)"
      >
        <video
          id="remoteVideo"
          class="bg-dark"
          :style="style"
          autoplay
          playsinline
          muted="muted"
          ref="remoteVideo"
        ></video>
        <audio class="hide" id="remoteAudio" autoplay playsinline />
        <q-card-actions align="right">
          <q-btn flat round color="red" icon="favorite" />
          <q-btn flat round color="teal" icon="bookmark" />
          <q-btn flat round color="primary" icon="share" />
        </q-card-actions>
      </q-card>
    </div>
    <q-resize-observer @resize="onResize" />
  </q-page>
</template>

<script setup>
import adapter from "webrtc-adapter";
import Janus from "janus-gateway";
import { ref, onMounted, reactive, onUnmounted } from "vue";
import { server, iceServers } from "src/utils/const";
import { useQuasar } from "quasar";
import { getRoomByID } from "src/api/rooms";
import { useUserStore } from "src/stores/user";

defineOptions({
  name: "RoomPage",
});
const { room } = defineProps(["room"]);
const $q = useQuasar();

const myusername = ref(null); ///登录

const userStore = useUserStore();
myusername.value = userStore.getUID();

const style = ref({ width: "200px", height: "200px" });

const initValue = {};
var room_pin = null;
const myid = ref(null);
const mypvtid = ref(null);
const room_name = ref(null);
const room_id = ref(null);
var number_in_room = 0;

const feedStreams = reactive(initValue);
var subStreams = {};
const feeds = reactive({});
var slots = {};
const subscriptions = reactive(initValue);
const localTracks = reactive(initValue);
const remoteTracks = reactive(initValue);
const localVideo = ref(null);
const remoteVideo = ref(null);
const remoteAudio = ref(null);

const plugin = "janus.plugin.videoroom";
var opaqueId = "videoroom-" + Janus.randomString(12);
var janus = null; // 保存janus实例
var pluginHandler = null; // 保存插件句柄
var remoteFeed = null;
var creatingSubscription = false;

function onResize(size) {
  if ($q.platform.is.mobile) {
    style.value = {
      width: size.width - 40 + "px",
      height: size.width - 40 + "px",
    };
  } else {
    style.value = {
      width: size.width / 3 - 40 + "px",
      height: (size.height - 170) / 2 - 20 + "px",
    };
  }
}

// janus初始化
const initJanus = function () {
  Janus.init({
    debug: "all",
    dependencies: Janus.useDefaultDependencies({
      adapter: adapter,
    }),
    callback: () => {
      if (!Janus.isWebrtcSupported()) {
        console.log("is not Supported Webrtc!");
        $q.notify({
          message: "不支持WebRtc!",
          color: "primary",
        });
        return;
      }
      console.log("初始化完毕");
      createSession();
    },
  });
};

// 创建会话
const createSession = function () {
  janus = new Janus({
    server: server,
    iceServers: iceServers,
    success: function () {
      console.log("success", janus.getServer());
      attachVideoRoomPlugin();
    },
    error: function (error) {
      Janus.error(error);
      $q.notify({
        message: error,
        icon: "announcement",
        color: "red",
      });
    },
    destroyed: function () {
      console.log("destroyed");
    },
  });
};

//附件VideoRoom插件
const attachVideoRoomPlugin = function () {
  janus.attach({
    plugin: plugin,
    opaqueId: opaqueId,
    success: function (pluginHandle) {
      pluginHandler = pluginHandle;
      Janus.log(
        "Plugin attached! (" +
          pluginHandler.getPlugin() +
          ", id=" +
          pluginHandler.getId() +
          ")"
      );
      Janus.log("  -- This is a publisher/manager");
      registerUsername();
    },
    error: function (error) {
      Janus.error("  -- Error attaching plugin...", error);
      $q.notify({
        message: "  -- Error attaching plugin... " + error,
        color: "red",
      });
    },
    consentDialog: function (on) {
      Janus.debug("Consent dialog should be " + (on ? "on" : "off") + " now");
    },
    iceState: function (state) {
      Janus.log("ICE state changed to " + state);
    },
    mediaState: function (medium, on, mid) {
      Janus.log(
        "Janus " +
          (on ? "started" : "stopped") +
          " receiving our " +
          medium +
          " (mid=" +
          mid +
          ")"
      );
    },
    webrtcState: function (on) {
      Janus.log(
        "Janus says our WebRTC PeerConnection is " +
          (on ? "up" : "down") +
          " now"
      );
    },
    slowLink: function (uplink, lost, mid) {
      Janus.warn(
        "Janus reports problems " +
          (uplink ? "sending" : "receiving") +
          " packets on mid " +
          mid +
          " (" +
          lost +
          " lost packets)"
      );
    },
    onmessage: function (msg, jsep) {
      Janus.debug(" ::: Got a message (publisher) :::", msg);
      let event = msg["videoroom"];
      Janus.log(" ::: event :::", event);
      if (event != undefined && event != null) {
        if (event === "joined") {
          handleJoined(msg);
        } else if (event === "destroyed") {
          Janus.warn("The room has been destroyed!");
          $q.dialog({
            title: "提示:",
            message: "会议室已经被销毁",
          }).onOk(() => {
            window.location.reload();
          });
        } else if (event === "event") {
          /// swiched
          if (msg["streams"]) {
            let streams = msg["streams"];
            for (let i in streams) {
              let stream = streams[i];
              stream["id"] = myid;
              stream["display"] = myusername;
            }
            feedStreams[myid] = {
              id: myid,
              display: myusername,
              streams: streams,
            };
          } else if (msg["publishers"]) {
            //会议室其他人接收到新入人员的信息
            handlePublisherStreams(msg);
          } else if (msg["leaving"]) {
            //发布者离开会议室
            let leaving = msg["leaving"]; //离开者id
            Janus.log("Publisher left: " + leaving);
            unsubscribeFrom(leaving); //取消订阅
          } else if (msg["unpublished"]) {
            // One of the publishers has unpublished?
            let unpublished = msg["unpublished"];
            Janus.log("Publisher left: " + unpublished);
            if (unpublished === "ok") {
              // 本人停止发布
              pluginHandler.hangup();
              return;
            }
            unsubscribeFrom(unpublished);
          } else if (msg["error"]) {
            Janus.log("error_code" + msg["error_code"]);
            $q.dialog({
              title: "提示:",
              message: msg["error"],
            });
          }
        }
      }
      if (jsep) {
        Janus.debug("Handling SDP as well...", jsep);
        pluginHandler.handleRemoteJsep({ jsep: jsep });
      }
    },
    onlocaltrack: function (track, on) {
      Janus.debug(" ::: Got a local track event :::");
      Janus.debug("Local track " + (on ? "added" : "removed") + ":", track);
      console.log(track.id);
      let trackId = track.id.replace(/[{}]/g, "");
      if (!on) {
        let stream = localTracks[trackId];
        if (stream) {
          try {
            let tracks = stream.getTracks();
            for (let i in tracks) {
              let mst = tracks[i];
              if (mst !== null && mst !== undefined) mst.stop();
            }
          } catch (e) {}
        }
        delete localTracks[trackId];
        return;
      }

      let stream = localTracks[trackId];
      if (stream) {
        return;
      }

      if (track.kind === "video") {
        stream = new MediaStream([track]);
        localTracks[trackId] = stream;
        Janus.log("Created local stream:", stream);
        Janus.attachMediaStream(localVideo.value, stream);
      }

      if (
        pluginHandler.webrtcStuff &&
        pluginHandler.webrtcStuff.pc.iceConnectionState !== "completed" &&
        pluginHandler.webrtcStuff.pc.iceConnectionState !== "connected"
      ) {
        Janus.log("Publish........");
      }
    },
    onremotetrack: function (track, mid, on, metadata) {
      // The publisher stream is sendonly, we don't expect anything here
    },
    ondataopen: function (label, protocol) {
      Janus.log("The DataChannel is available!");
    },
    ondata: function (data) {
      Janus.debug("We got data from the DataChannel!", data);
    },
    oncleanup: function () {
      Janus.log(" ::: Got a cleanup notification: we are unpublished now :::");

      delete feedStreams[myid];

      Object.assign(localTracks, initValue);

      Janus.log(" ::: Got a cleanup notification :::");
    },
  });
};

//加入会议室
function handleJoined(msg) {
  myid.value = msg["id"];
  mypvtid.value = msg["private_id"];
  room_name.value = msg["description"];
  room_id.value = msg["room"];

  Janus.log(
    "Successfully joined room " +
      msg["room"] +
      " with ID " +
      myid.value +
      " numbers:" +
      number_in_room
  );

  publishOwnFeed(true);

  // Any new feed to attach to?
  if (msg["publishers"] && msg["publishers"].length > 0) {
    handlePublisherStreams(msg);
  }
}

function handlePublisherStreams(msg) {
  let list = msg["publishers"];
  Janus.debug("Got a list of available publishers/feeds:", list);

  let sources = [];

  for (let user of list) {
    if (user["dummy"]) continue;
    let id = user["id"];
    let display = user["display"];
    let streams = user["streams"];

    for (let i in streams) {
      let stream = streams[i];
      stream["id"] = id;
      stream["display"] = display;
    }

    let slot = feedStreams[id] ? feedStreams[id].slot : null;
    let remoteVideos = feedStreams[id] ? feedStreams[id].remoteVideos : 0;
    feedStreams[id] = {
      id: id,
      display: display,
      streams: streams,
      slot: slot,
      remoteVideos: remoteVideos,
    };
    console.log(JSON.stringify(feedStreams));
    Janus.debug("  >> [" + id + "] " + display + ":", streams);
    sources.push(streams);
  }
  if (sources.length > 0) subscribeTo(sources);
}

function unsubscribeFrom(id) {
  // Unsubscribe from this publisher
  let feed = feedStreams[id];
  if (!feed) return;
  Janus.debug(
    "Feed " + id + " (" + feed.display + ") has left the room, detaching"
  );

  delete feedStreams[id];
  delete feeds[feed.slot];
  // feeds.slot = 0;
  // Send an unsubscribe request
  let unsubscribe = {
    request: "unsubscribe",
    streams: [{ feed: id }],
  };
  if (remoteFeed != null) remoteFeed.send({ message: unsubscribe });
  delete subscriptions[id];
}
// 发布视频
function publishOwnFeed(useAudio) {
  // Publish our stream
  let tracks = [];
  if (useAudio) tracks.push({ type: "audio", capture: true, recv: false });
  tracks.push({
    type: "video",
    capture: true,
    recv: false,
  });
  //~ tracks.push({ type: 'data' });

  pluginHandler.createOffer({
    tracks: tracks,
    success: function (jsep) {
      Janus.debug("Got publisher SDP!");
      Janus.debug(jsep);
      let publish = { request: "configure", audio: useAudio, video: true };
      pluginHandler.send({ message: publish, jsep: jsep });
    },
    error: function (error) {
      Janus.error("WebRTC error:", error);
      if (useAudio) {
        publishOwnFeed(false);
      } else {
        $q.notify({
          message: "WebRTC error... " + error.message,
          color: "red",
        });
      }
    },
  });
}
///订阅视频
function subscribeTo(sources) {
  // Check if we're still creating the subscription handle
  if (creatingSubscription) {
    // Still working on the handle, send this request later when it's ready
    setTimeout(function () {
      subscribeTo(sources);
    }, 500);
    return;
  }

  if (remoteFeed) {
    handleSubscribeStreams(sources);
    return;
  }

  attachRemoteVideoRoomPlugin(sources);
}

function attachRemoteVideoRoomPlugin(sources) {
  // If we got here, we're creating a new handle for the subscriptions (we only need one)
  creatingSubscription = true;
  console.log("attachRemoteVideoRoomPlugin", sources);
  janus.attach({
    plugin: "janus.plugin.videoroom",
    opaqueId: opaqueId,
    success: function (pluginHandle) {
      remoteFeed = pluginHandle;
      Object.assign(remoteTracks, initValue);

      Janus.log(
        "remote Plugin attached! (" +
          remoteFeed.getPlugin() +
          ", id=" +
          remoteFeed.getId() +
          ")"
      );
      Janus.log("  -- This is a multistream subscriber");
      handleSubscribeStreams(sources);
    },
    error: function (error) {
      Janus.error("  -- Error attaching plugin...", error);
      $q.notify({
        message: "Error attaching plugin: " + error,
        color: "red",
      });
    },
    iceState: function (state) {
      Janus.log("ICE state (remote feed) changed to " + state);
    },
    webrtcState: function (on) {
      Janus.log(
        "Janus says this WebRTC PeerConnection (remote feed) is " +
          (on ? "up" : "down") +
          " now"
      );
    },
    slowLink: function (uplink, lost, mid) {
      Janus.warn(
        "Janus reports problems " +
          (uplink ? "sending" : "receiving") +
          " packets on mid " +
          mid +
          " (" +
          lost +
          " lost packets)"
      );
    },
    onmessage: function (msg, jsep) {
      Janus.debug(" ::: Got a message (subscriber) :::", msg);
      let event = msg["videoroom"];
      Janus.debug("Event: " + event);
      if (msg["error"]) {
        $q.dialog({
          title: "错误提示:",
          message: msg["error"],
        });
      } else if (event) {
        if (event === "attached") {
          creatingSubscription = false;
          Janus.log("Successfully attached to feed in room " + msg["room"]);
        } else if (event === "event") {
          Janus.log(JSON.stringify(msg));
        } else {
          Janus.log(JSON.stringify(msg));
        }
      }
      if (msg["streams"]) {
        for (let i in msg["streams"]) {
          let mid = msg["streams"][i]["mid"];
          subStreams[mid] = msg["streams"][i];
          let feed = feedStreams[msg["streams"][i]["feed_id"]];
          if (feed && feed.slot) {
            slots[mid] = feed.slot;
          }
        }
        Janus.debug("subStreams:", subStreams);
      }
      if (jsep) {
        Janus.debug("Handling SDP as well...", jsep);
        // Answer and attach
        remoteFeed.createAnswer({
          jsep: jsep,
          tracks: [{ type: "data" }],
          success: function (jsep) {
            Janus.debug("Got SDP!");
            Janus.debug(jsep);
            let body = { request: "start", room: room_id.value };
            remoteFeed.send({ message: body, jsep: jsep });
          },
          error: function (error) {
            Janus.error("WebRTC error:", error);
            $q.notify({
              message: "WebRTC error... " + error.message,
              color: "red",
            });
          },
        });
      }
    },
    // eslint-disable-next-line no-unused-vars
    onlocaltrack: function (track, on) {
      // The subscriber stream is recvonly, we don't expect anything here
    },
    onremotetrack: function (track, mid, on, metadata) {
      Janus.debug(
        "Remote track (mid=" +
          mid +
          ") " +
          (on ? "added" : "removed") +
          (metadata ? " (" + metadata.reason + ") " : "") +
          ":",
        track
      );
      // Which publisher are we getting on this mid?
      let sub = subStreams[mid];
      let feed = feedStreams[sub.feed_id];
      Janus.debug(
        " >> This track is coming from feed " + sub.feed_id + ":",
        feed
      );
      let slot = slots[mid];
      if (feed && !slot) {
        slot = feed.slot;
        slots[mid] = feed.slot;
      }

      Janus.debug(" >> mid " + mid + " is in slot " + slot);
      if (!on) {
        if (track.kind === "video" && feed) {
          feed.remoteVideos--;
        }
        delete remoteTracks[mid];
        delete slots[mid];
        // remoteAudio.value = null;
        // remoteVideo.value = null;
        return;
      }
      // If we're here, a new track was added
      if (track.kind === "audio") {
        // New audio track: create a stream out of it, and use a hidden <audio> element
        let stream = new MediaStream([track]);
        remoteTracks[mid] = stream;
        Janus.log("Created remote audio stream:", stream);

        Janus.attachMediaStream(document.getElementById("remoteAudio"), stream);
      } else {
        // New video track: create a stream out of it
        feed.remoteVideos++;

        let stream = new MediaStream([track]);
        remoteTracks[mid] = stream;
        Janus.log("Created remote video stream:", stream);

        Janus.attachMediaStream(document.getElementById("remoteVideo"), stream);
      }
    },
    oncleanup: function () {
      Janus.log(" ::: Got a cleanup notification (remote feed) :::");
      for (let i = 1; i < number_in_room; i++) {
        feedStreams[i].remoteVideos = 0;
      }
      Object.assign(remoteTracks, initValue);
    },
  });
}

function handleSubscribeStreams(sources) {
  let added = [],
    removed = [];
  for (let s in sources) {
    let streams = sources[s];
    for (let i in streams) {
      let stream = streams[i];
      // If the publisher is VP8/VP9 and this is an older Safari, let's avoid video
      if (
        stream.type === "video" &&
        Janus.webRTCAdapter.browserDetails.browser === "safari" &&
        ((stream.codec === "vp9" && !Janus.safariVp9) ||
          (stream.codec === "vp8" && !Janus.safariVp8))
      ) {
        $q.notify({
          message:
            "Publisher is using " +
            stream.codec.toUpperCase +
            ", but Safari doesn't support it: disabling video stream #" +
            stream.mindex,
          color: "red",
        });
        continue;
      }
      if (stream.disabled) {
        Janus.log("Disabled stream:", stream);
        // Unsubscribe
        if (!creatingSubscription) {
          removed.push({
            feed: stream.id,
            mid: stream.mid,
          });
          delete subscriptions[stream.id][stream.mid];
        }
        continue;
      }
      if (subscriptions[stream.id] && subscriptions[stream.id][stream.mid]) {
        Janus.log("Already subscribed to stream, skipping:", stream);
        continue;
      }
      // Find an empty slot in the UI for each new source
      if (!feedStreams[stream.id].slot) {
        for (let slot = 1; slot < number_in_room; slot++) {
          if (!feeds[slot]) {
            feeds[slot] = stream.id;
            feedStreams[stream.id].slot = slot;
            feedStreams[stream.id].remoteVideos = 0;
            break;
          }
        }
      }
      console.log("00000000000:", feeds);
      // Subscribe
      added.push({ feed: stream.id, mid: stream.mid });
      if (!subscriptions[stream.id]) {
        subscriptions[stream.id] = {};
      }
      subscriptions[stream.id][stream.mid] = true;
    }
  }
  if (added.length === 0 && removed.length === 0) {
    return;
  }
  if (creatingSubscription) {
    //增加
    let subscribe = {
      request: "join",
      room: room_id.value,
      ptype: "subscriber",
      streams: added,
      private_id: mypvtid.value,
    };
    if(room_pin && room_pin.length > 0){
      subscribe["pin"] = room_pin;
    }
    remoteFeed.send({ message: subscribe });
  } else {
    // 修改
    let update = { request: "update" };
    if (added) update.subscribe = added;
    if (removed) update.unsubscribe = removed;

    remoteFeed.send({ message: update });
  }
}

function registerUsername() {
  let register = {
    request: "join",
    room: parseInt(room),
    ptype: "publisher",
    display: myusername.value + "",
  };
  if(room_pin && room_pin.length > 0){
    register["pin"] = room_pin;
  }

  pluginHandler.send({ message: register });
}

function unpublishOwnFeed() {
  // Unpublish our stream
  let unpublish = { request: "unpublish" };
  pluginHandler.send({ message: unpublish });
}

function getVideoRoom() {
  const params = {
    room_id: room,
  };
  getRoomByID(params)
    .then((res) => {
      room_name.value = res.description;
      room_pin = res.pin;
      number_in_room = res.numbers;
    })
    .catch((error) => {
      Janus.log(error);
    });
}

onMounted(() => {
  getVideoRoom();
  initJanus();
});

onUnmounted(() => {
  pluginHandler.hangup();
  pluginHandler.detach();
  janus.destroy();
});
</script>

<style lang="scss" scoped>
.q-card__actions {
  padding: 0px;
}
</style>
